From 5971b2c39a4856bf119510aa37e9c1ee82ea7e1e Mon Sep 17 00:00:00 2001 From: Adam Roses Wight Date: Sun, 18 May 2014 00:04:36 -0700 Subject: [PATCH] API: Enforce section=new constraint when using 'redirect' mode Finish implementation of the 'redirect' edit API parameter. This flag should only be used as a convenience when adding a new section, appending, or prepending. Any other usage must be done using the long-cut (fetch page, determine if it is a redirect, follow redirects and edit the target page's content). This patch takes apart the "EditConflict_redirect" test, because it no longer makes sense--unless you dear reader can figure out how to force an edit conflict when only adding a section? Bug: 24330 Change-Id: Ie3f7273c18e156da1e50e1a36aac2e5341710982 --- includes/api/ApiEditPage.php | 9 +- .../phpunit/includes/api/ApiEditPageTest.php | 142 ++++++++++++++---- 2 files changed, 117 insertions(+), 34 deletions(-) diff --git a/includes/api/ApiEditPage.php b/includes/api/ApiEditPage.php index 635f6f889c..cb0f8c2291 100644 --- a/includes/api/ApiEditPage.php +++ b/includes/api/ApiEditPage.php @@ -48,6 +48,9 @@ class ApiEditPage extends ApiBase { $apiResult = $this->getResult(); if ( $params['redirect'] ) { + if ( $params['prependtext'] === null && $params['appendtext'] === null && $params['section'] !== 'new' ) { + $this->dieUsage( 'You have attempted to edit using the "redirect"-following mode, which must be used in conjuction with section=new, prependtext, or appendtext.', 'redirect-appendonly' ); + } if ( $titleObj->isRedirect() ) { $oldTitle = $titleObj; @@ -526,7 +529,7 @@ class ApiEditPage extends ApiBase { array( 'editconflict' ), array( 'emptynewsection' ), array( 'unknownerror', 'retval' ), - array( 'code' => 'nosuchsection', 'info' => 'There is no section section.' ), + array( 'code' => 'nosuchsection', 'info' => 'There is no such section.' ), array( 'code' => 'invalidsection', 'info' => 'The section parameter must be a valid section id or \'new\'' @@ -542,6 +545,10 @@ class ApiEditPage extends ApiBase { array( 'code' => 'appendnotsupported', 'info' => 'This type of page can not be edited by appending or prepending text.' ), + array( + 'code' => 'redirect-appendonly', + 'info' => 'You have attempted to edit using the "redirect"-following mode, which must be used in conjuction with section=new, prependtext, or appendtext.', + ), array( 'code' => 'badformat', 'info' => 'The requested serialization format can not be applied to the page\'s content model' diff --git a/tests/phpunit/includes/api/ApiEditPageTest.php b/tests/phpunit/includes/api/ApiEditPageTest.php index 2e1c265a86..9f8c13994e 100644 --- a/tests/phpunit/includes/api/ApiEditPageTest.php +++ b/tests/phpunit/includes/api/ApiEditPageTest.php @@ -274,6 +274,100 @@ class ApiEditPageTest extends ApiTestCase { $this->assertEquals( "== header ==\n\ntest\n\n== header ==\n\ntest", $text ); } + /** + * Ensure we can edit through a redirect, if adding a section + */ + public function testEdit_redirect() { + static $count = 0; + $count++; + + // assume NS_HELP defaults to wikitext + $name = "Help:ApiEditPageTest_testEdit_redirect_$count"; + $title = Title::newFromText( $name ); + $page = WikiPage::factory( $title ); + + $rname = "Help:ApiEditPageTest_testEdit_redirect_r$count"; + $rtitle = Title::newFromText( $rname ); + $rpage = WikiPage::factory( $rtitle ); + + // base edit for content + $page->doEditContent( new WikitextContent( "Foo" ), + "testing 1", EDIT_NEW, false, self::$users['sysop']->user ); + $this->forceRevisionDate( $page, '20120101000000' ); + $baseTime = $page->getRevision()->getTimestamp(); + + // base edit for redirect + $rpage->doEditContent( new WikitextContent( "#REDIRECT [[$name]]" ), + "testing 1", EDIT_NEW, false, self::$users['sysop']->user ); + $this->forceRevisionDate( $rpage, '20120101000000' ); + + // conflicting edit to redirect + $rpage->doEditContent( new WikitextContent( "#REDIRECT [[$name]]\n\n[[Category:Test]]" ), + "testing 2", EDIT_UPDATE, $page->getLatest(), self::$users['uploader']->user ); + $this->forceRevisionDate( $rpage, '20120101020202' ); + + // try to save edit, following the redirect + list( $re, , ) = $this->doApiRequestWithToken( array( + 'action' => 'edit', + 'title' => $rname, + 'text' => 'nix bar!', + 'basetimestamp' => $baseTime, + 'section' => 'new', + 'redirect' => true, + ), null, self::$users['sysop']->user ); + + $this->assertEquals( 'Success', $re['edit']['result'], + "no problems expected when following redirect" ); + } + + /** + * Ensure we cannot edit through a redirect, if attempting to overwrite content + */ + public function testEdit_redirectText() { + static $count = 0; + $count++; + + // assume NS_HELP defaults to wikitext + $name = "Help:ApiEditPageTest_testEdit_redirectText_$count"; + $title = Title::newFromText( $name ); + $page = WikiPage::factory( $title ); + + $rname = "Help:ApiEditPageTest_testEdit_redirectText_r$count"; + $rtitle = Title::newFromText( $rname ); + $rpage = WikiPage::factory( $rtitle ); + + // base edit for content + $page->doEditContent( new WikitextContent( "Foo" ), + "testing 1", EDIT_NEW, false, self::$users['sysop']->user ); + $this->forceRevisionDate( $page, '20120101000000' ); + $baseTime = $page->getRevision()->getTimestamp(); + + // base edit for redirect + $rpage->doEditContent( new WikitextContent( "#REDIRECT [[$name]]" ), + "testing 1", EDIT_NEW, false, self::$users['sysop']->user ); + $this->forceRevisionDate( $rpage, '20120101000000' ); + + // conflicting edit to redirect + $rpage->doEditContent( new WikitextContent( "#REDIRECT [[$name]]\n\n[[Category:Test]]" ), + "testing 2", EDIT_UPDATE, $page->getLatest(), self::$users['uploader']->user ); + $this->forceRevisionDate( $rpage, '20120101020202' ); + + // try to save edit, following the redirect but without creating a section + try { + $this->doApiRequestWithToken( array( + 'action' => 'edit', + 'title' => $rname, + 'text' => 'nix bar!', + 'basetimestamp' => $baseTime, + 'redirect' => true, + ), null, self::$users['sysop']->user ); + + $this->fail( 'redirect-appendonly error expected' ); + } catch ( UsageException $ex ) { + $this->assertEquals( 'redirect-appendonly', $ex->getCodeString() ); + } + } + public function testEditConflict() { static $count = 0; $count++; @@ -310,60 +404,41 @@ class ApiEditPageTest extends ApiTestCase { } } - public function testEditConflict_redirect() { + /** + * Ensure that editing using section=new will prevent simple conflicts + */ + public function testEditConflict_newSection() { static $count = 0; $count++; // assume NS_HELP defaults to wikitext - $name = "Help:ApiEditPageTest_testEditConflict_redirect_$count"; + $name = "Help:ApiEditPageTest_testEditConflict_newSection_$count"; $title = Title::newFromText( $name ); - $page = WikiPage::factory( $title ); - $rname = "Help:ApiEditPageTest_testEditConflict_redirect_r$count"; - $rtitle = Title::newFromText( $rname ); - $rpage = WikiPage::factory( $rtitle ); + $page = WikiPage::factory( $title ); - // base edit for content + // base edit $page->doEditContent( new WikitextContent( "Foo" ), "testing 1", EDIT_NEW, false, self::$users['sysop']->user ); $this->forceRevisionDate( $page, '20120101000000' ); $baseTime = $page->getRevision()->getTimestamp(); - // base edit for redirect - $rpage->doEditContent( new WikitextContent( "#REDIRECT [[$name]]" ), - "testing 1", EDIT_NEW, false, self::$users['sysop']->user ); - $this->forceRevisionDate( $rpage, '20120101000000' ); - - // conflicting edit to redirect - $rpage->doEditContent( new WikitextContent( "#REDIRECT [[$name]]\n\n[[Category:Test]]" ), + // conflicting edit + $page->doEditContent( new WikitextContent( "Foo bar" ), "testing 2", EDIT_UPDATE, $page->getLatest(), self::$users['uploader']->user ); - $this->forceRevisionDate( $rpage, '20120101020202' ); + $this->forceRevisionDate( $page, '20120101020202' ); - // try to save edit; should work, because we follow the redirect + // try to save edit, expect no conflict list( $re, , ) = $this->doApiRequestWithToken( array( 'action' => 'edit', - 'title' => $rname, + 'title' => $name, 'text' => 'nix bar!', 'basetimestamp' => $baseTime, - 'redirect' => true, + 'section' => 'new', ), null, self::$users['sysop']->user ); $this->assertEquals( 'Success', $re['edit']['result'], - "no edit conflict expected when following redirect" ); - - // try again, without following the redirect. Should fail. - try { - $this->doApiRequestWithToken( array( - 'action' => 'edit', - 'title' => $rname, - 'text' => 'nix bar!', - 'basetimestamp' => $baseTime, - ), null, self::$users['sysop']->user ); - - $this->fail( 'edit conflict expected' ); - } catch ( UsageException $ex ) { - $this->assertEquals( 'editconflict', $ex->getCodeString() ); - } + "no edit conflict expected here" ); } public function testEditConflict_bug41990() { @@ -405,6 +480,7 @@ class ApiEditPageTest extends ApiTestCase { 'action' => 'edit', 'title' => $rname, 'text' => 'nix bar!', + 'section' => 'new', 'redirect' => true, ), null, self::$users['sysop']->user ); -- 2.20.1